﻿#include "../../ctlList.h"
#include <algorithm>

namespace swui
{
	ctlStyleGradient::_subType _fetch_color_style(const ctlStyleBase& base)
	{
		switch(style_cast<const ctlStyleList&>(base).subStyle)
		{
		case ctlStyleList::orange:
		case ctlStyleList::gorange:
		case ctlStyleList::lgorange:
			return ctlStyleGradient::orange;
		}
		return ctlStyleGradient::common;
	}
	bool ctlList::create(ctlRoot* parent, ctlRect rect, const ctlStyleList& style)
	{
		if(parent == 0 || isCreated() || !parent->isCreated()) return false; 
		std::lock_guard<decltype(_eventTrans)> gurad(
			_eventTrans.getControlEventTrans(parent));
		_control_virtual_class_end = this;
		_basic_create_(rect, parent);
		_controlType = ctlControlType::ctlList;
		_virtual_font_create();
		enableBackground(true);
		setLineSelect(true);
		setStyle(style);
		_list_key_affect = keyAffect::scroll;
		_list_overflow_x = _list_overflow_y = ctlOverflowScroll::automatic;
		_list_total_height = _list_total_width = 0;
		_list_top_offset = _list_top_pos = 0;
		_list_left_offset = 0;
		_list_is_double_click_edit = false;
		_list_header_enable = false;
		_list_select_cell_info = getCellInfo(~0, ~0);
		//创建表头
		_list_header.create(this, ctlRect(0, 0, 1, getFont()->getFontHeight() + 10));
		_list_header.setFocusAssign(this);
		_list_header.enableBackground(true);
		_list_header.setBkFillColor(0xffffffff);
		_list_header.setBkBorderStyle(IBackground::borderStyle::empty);
		_list_header.setBackgroundCombineRgnEnable(false);
		_list_header.setZOrderTop();
		_list_header.hide();
		//创建表头补足按钮
		ctlButton *btn = new(_list_header.heapAlloc(sizeof(ctlButton), 
			ctlDestructor<ctlButton>::deleter))ctlButton;
		btn->create(&_list_header, ctlRect(0, 0, 1, 1));
		btn->setFocusAssign(this);
		btn->setPercentSizeFormat(ctlDirection::y, 0.0f, 1.0f);
		btn->setRelativeSizeFormat(ctlDirection::x, 0, 0, &_list_header, nullptr);
		btn->setAsStatic(true);
		//创建垂直滚动条
		_list_scroll_vertical.create(this, ctlPoint(0, 0), 100, ctlScrollDirection::vertical, 
			ctlStyleGradient(_fetch_color_style(style)).changeToScroll(), 
			ctlStyleGradient(_fetch_color_style(style)));
		_list_scroll_vertical.setFocusAssign(this);
		_list_scroll_vertical.setZOrderTop();
		_list_scroll_vertical.setRelativeMoveFormat(ctlDirection::x, 
			0, 0, this, this, moveAlignment::rightTop);
		_list_scroll_vertical.addLambdaEvent(ctlNotifyEventType::scroll,
			[this](ctlRoot* object, ctlNotifyEvent* ne)
			{
				if(getRowNumber() == 0) return;
				ctlNotifyScroll* ns = reinterpret_cast<ctlNotifyScroll*>(ne->info);
				int delta = static_cast<int>(ns->logicLength - 
					_list_scroll_vertical.getScrolledLogicLength());
				auto result = _list_map_point_to_pos(ctlPoint(0, delta + _list_get_body_top()));
				_list_top_offset = result.second;
				_list_top_pos = result.first;
				_check_line_position(true);
			});
		//创建水平滚动条
		_list_scroll_horizontal.create(this, ctlPoint(0, 0), 100, ctlScrollDirection::horizontal,
			ctlStyleGradient(_fetch_color_style(style)).changeToScroll(), 
			ctlStyleGradient(_fetch_color_style(style)));
		_list_scroll_horizontal.setFocusAssign(this);
		_list_scroll_horizontal.setZOrderTop();
		_list_scroll_horizontal.setRelativeMoveFormat(ctlDirection::y, 
			0, 0, this, this, moveAlignment::leftBottom);
		_list_scroll_horizontal.addLambdaEvent(ctlNotifyEventType::scroll,
			[this](ctlRoot* object, ctlNotifyEvent* ne)
			{
				if(getColumnNumber() == 0) return;
				ctlNotifyScroll* ns = reinterpret_cast<ctlNotifyScroll*>(ne->info);
				_list_left_offset = ns->logicLength;
				_check_line_position(true);
			});
		_check_scroll();
		return true;
	}
	bool ctlList::isDoubleClickEdit()
	{
		return _list_is_double_click_edit;
	}
	void ctlList::setDoubleClickEdit(bool v)
	{
		_list_is_double_click_edit = v;
	}
	void ctlList::setOverflowX(ctlOverflowScroll over)
	{
		_list_overflow_x = over;
		_check_scroll();
	}
	void ctlList::setOverflowY(ctlOverflowScroll over)
	{
		_list_overflow_y = over;
		_check_scroll();
	}
	ctlOverflowScroll ctlList::getOverflowX()
	{
		return _list_overflow_x;
	}
	ctlOverflowScroll ctlList::getOverflowY()
	{
		return _list_overflow_y;
	}
	bool ctlList::isLineSelect()
	{
		return _list_is_line_selecte;
	}
	void ctlList::setLineSelect(bool bol)
	{
		_list_is_line_selecte = bol;
	}
	unsigned ctlList::getRowNumber()
	{
		return _list_row_info.size();
	}
	unsigned ctlList::getColumnNumber()
	{
		return _list_column_info.size();
	}
	unsigned ctlList::getRowHeight()
	{
		return _list_total_height;
	}
	unsigned ctlList::getColumnWidth()
	{
		return _list_total_width;
	}
	bool ctlList::insertColumn(unsigned index, unsigned width)
	{
		if(index > getColumnNumber() || width == 0)
			return false;
		if(_list_send_event(ctlNotifyList::insertColumn, 
			nullptr, index, &width) == false) return false;
		columnInfo info;
		info.width = width;
		_list_total_width += width - 1;
		if(index <= _list_select_cell_info.column)
			++_list_select_cell_info.column;
		for(const rowInfo& elem : _list_row_info)
		{
			ctlSingleLabel* ctrl = new(elem.ctrl->heapAlloc(sizeof(ctlSingleLabel), 
				ctlDestructor<ctlSingleLabel>::deleter))ctlSingleLabel;
			ctrl->create(elem.ctrl, ctlRect(0, 0, 1, 1));
			ctrl->setNotify(true);
			ctrl->setFocusAssign(this);
			ctrl->enableBackground(true);
			ctrl->setBkFillStyle(IBackground::fillStyle::empty);
			ctrl->setBkBorderDirectory(0x05);
			ctrl->setFont(getFontIndex());
			ctrl->size(width, 1);
			ctrl->setPercentSizeFormat(ctlDirection::y, 0.0f, 1.0f);
			ctrl->setMouseEventModel(ctlMouseEventModel::capture);
			if(index != 0)
			{
				ctlRoot* pre = elem.ctrl->getChild(index - 1);
				ctrl->setRelativeMoveFormat(ctlDirection::x, -1, 0, 
					pre, pre, moveAlignment::leftTop, moveAlignment::rightTop);
				ctrl->setZOrderAfter(pre);
				ctlRoot* next = elem.ctrl->getChild(index + 1);
				if(next)
				{
					next->setRelativeMoveFormat(ctlDirection::x, -1, 0, 
						ctrl, ctrl, moveAlignment::leftTop, moveAlignment::rightTop);
					next->setZOrderAfter(ctrl);
				}
			} else if(getColumnNumber() != 0) {
				ctlRoot* first = elem.ctrl->getChild(0);
				ctrl->move(0, first->getTop());
				ctrl->setZOrderBefore(first);
				first->setRelativeMoveFormat(ctlDirection::x, -1, 0, 
					ctrl, ctrl, moveAlignment::leftTop, moveAlignment::rightTop);
			}
		}
		//创建表头按钮
		ctlButton *btn = new(_list_header.heapAlloc(sizeof(ctlButton), 
			ctlDestructor<ctlButton>::deleter))ctlButton;
		btn->create(&_list_header, ctlRect(0, 0, info.width, 1), "",
			ctlStyleGradient(_fetch_color_style(getStyleRef())));
		btn->setFocusAssign(this);
		btn->setPercentSizeFormat(ctlDirection::y, 0.0f, 1.0f);
		if(index != 0)
		{
			ctlRoot* pre = _list_header.getChild(index - 1);
			btn->setRelativeMoveFormat(ctlDirection::x, -1, 0, 
				pre, pre, moveAlignment::leftTop, moveAlignment::rightTop);
			btn->setZOrderAfter(pre);
			ctlRoot* next = _list_header.getChild(index + 1);
			if(next)
			{
				next->setRelativeMoveFormat(ctlDirection::x, -1, 0, 
					btn, btn, moveAlignment::leftTop, moveAlignment::rightTop);
				next->setZOrderAfter(btn);
			}
		} else {
			ctlRoot* first = _list_header.getChild(0);
			btn->setZOrderBefore(first);
			first->setRelativeMoveFormat(ctlDirection::x, -1, 0, 
				btn, btn, moveAlignment::leftTop, moveAlignment::rightTop);
		}
		_list_column_info.insert(_list_column_info.begin() + index, info);
		_list_send_event(ctlNotifyList::afterInsertColumn, nullptr, index, nullptr);
		_check_line_position(true);
		_check_scroll(true);
		return true;
	}
	bool ctlList::insertRows(unsigned index, unsigned *height, unsigned number)
	{
		if(index > getRowNumber())
			return false;
		bool xf_v = false;
		if(_list_top_pos >= index) xf_v = true;
		for(unsigned num = 0; num != number; ++num, ++index)
		{
			rowInfo info;
			info.height = height[num] ? height[num] : (getFont()->getFontHeight() + 
				ctlSingleLabel::defaultPaddingRect.y + ctlSingleLabel::defaultPaddingRect.cy);
			if(_list_send_event(ctlNotifyList::insertRow, nullptr, index, &info.height) == false)
				continue;
			_list_total_height += info.height - 1;
			info.ctrl = new(heapAlloc(sizeof(ctlStatic), 
				ctlDestructor<ctlStatic>::deleter))ctlStatic;
			info.ctrl->create(this, ctlRect(0, _list_get_body_top(), 1, info.height));
			info.ctrl->setFocusAssign(this);
			if(index != 0)
			{
				ctlStatic* pre = getRowInfo(index - 1).ctrl;
			} else if(getRowNumber() != 0) {
				ctlRoot* first = getRowInfo(0).ctrl;
				info.ctrl->move(0, first->getTop());
			} else _list_top_offset = info.height;
			info.ctrl->enableBackground(true);
			info.ctrl->setBkFillStyle(IBackground::fillStyle::empty);
			info.ctrl->setBkBorderDirectory(0x0a);
			info.ctrl->addEvent(ctlMouseEventType::leftDown, _list_row_click_handler);
			info.ctrl->addEvent(ctlMouseEventType::leftDBClick, _list_row_dbclick_handler);
			info.ctrl->setNotify(true);
			info.ctrl->setBackgroundCombineRgnEnable(false);
			info.ctrl->hide();
			_list_row_info.insert(_list_row_info.begin() + index, info);
			if(index <= _list_select_cell_info.row)
				++_list_select_cell_info.row;
			//创建单元格
			ctlRoot* preCell = nullptr;
			for(const columnInfo& elem : _list_column_info)
			{
				ctlSingleLabel* ctrl = new(info.ctrl->heapAlloc(sizeof(ctlSingleLabel), 
					ctlDestructor<ctlSingleLabel>::deleter))ctlSingleLabel;
				ctrl->create(info.ctrl, ctlRect(0, 0, 1, 1));
				ctrl->setNotify(true);
				ctrl->setFocusAssign(this);
				ctrl->enableBackground(true);
				ctrl->setBkFillStyle(IBackground::fillStyle::empty);
				ctrl->setBkBorderDirectory(0x05);
				ctrl->setMouseEventModel(ctlMouseEventModel::capture);
				ctrl->setFont(getFontIndex());
				ctrl->size(elem.width, 1);
				ctrl->setPercentSizeFormat(ctlDirection::y, 0.0f, 1.0f);
				if(preCell != nullptr)
				{
					ctrl->setRelativeMoveFormat(ctlDirection::x, -1, 0,
						preCell, nullptr, moveAlignment::leftTop, moveAlignment::rightTop);
				}
				preCell = ctrl;
			}
			_list_send_event(ctlNotifyList::afterInsertRow, info.ctrl, index, nullptr);
		}
		_check_scroll(xf_v);
		_check_line_position(true);
		return true;
	}
	bool ctlList::insertRow(unsigned index, unsigned height)
	{
		return insertRows(index, &height, 1);
	}
	bool ctlList::removeRow(unsigned index)
	{
		if(index >= getRowNumber())
			return false;
		if(_list_send_event(ctlNotifyList::removeRow, nullptr, index, nullptr) == false)
			return false;
		if(_list_select_cell_info.row == index)
			_list_select_cell_info.ctrl = nullptr;
		if(_list_select_cell_info.row > index)
			--_list_select_cell_info.row;
		auto iter = _list_row_info.begin() + index;
		iter->ctrl->destroy();
		heapFree(iter->ctrl);
		_list_total_height -= getRowInfo(index).height - 1;
		_list_row_info.erase(iter);
		_list_send_event(ctlNotifyList::afterRemoveRow, nullptr, index, nullptr);
		_check_scroll(index < _list_top_pos);
		_check_line_position(true);
		return true;
	}
	bool ctlList::removeColumn(unsigned index)
	{
		if(index >= getColumnNumber())
			return false;
		if(_list_send_event(ctlNotifyList::removeColumn, nullptr, index, nullptr) == false)
			return false;
		if(_list_select_cell_info.column == index)
			_list_select_cell_info.ctrl = nullptr;
		if(_list_select_cell_info.column > index)
			--_list_select_cell_info.column;
		auto lambdaRemove = [&](ctlRoot* ctrl)
		{
			ctlRoot* next = ctrl->getChild(index + 1);
			if(next)
			{
				if(index == 0) 
				{
					next->setMoveFormat(moveFormat::absolute, ctlDirection::x);
					next->move(0, 0);
				} else {
					ctlRoot* pre = ctrl->getChild(index - 1);
					next->setRelativeMoveFormat(ctlDirection::x, -1, 0,
						pre, pre, moveAlignment::leftTop, moveAlignment::rightTop);
				}
			}
			ctlRoot* th = ctrl->getChild(index);
			th->destroy();
			ctrl->heapFree(th);
		};
		for(const rowInfo& elem : _list_row_info)
			lambdaRemove(elem.ctrl);
		lambdaRemove(&_list_header);
		_list_total_width -= getColumnInfo(index).width - 1;
		_list_column_info.erase(_list_column_info.begin() + index);
		_list_send_event(ctlNotifyList::afterRemoveColumn, nullptr, index, nullptr);
		_check_scroll(true);
		_check_line_position(true);
		return true;
	}
	bool ctlList::setRowHeight(unsigned index, unsigned height)
	{
		if(index >= getRowNumber())
			return false;
		rowInfo info = getRowInfo(index);
		unsigned pre = info.height;
		info.height = height == 0 ? (getFont()->getFontHeight() + 
			ctlSingleLabel::defaultPaddingRect.y + ctlSingleLabel::defaultPaddingRect.cy) : height;
		_list_total_height += info.height - pre;
		info.ctrl->size(info.ctrl->getWidth(), info.height);
		_list_row_info[index] = info;
		_check_scroll();
		_check_line_position(true);
		return true;
	}
	bool ctlList::setColumnWidth(unsigned index, unsigned width)
	{
		if(index >= getColumnNumber())
			return false;
		columnInfo info = getColumnInfo(index);
		_list_total_width += width - info.width;
		info.width = width;
		_list_column_info[index] = info;
		for(const rowInfo& elem : _list_row_info)
		{
			ctlRoot* ctrl = elem.ctrl->getChild(index);
			ctrl->size(width, ctrl->getHeight());
		}
		ctlRoot* ctrl = _list_header.getChild(index);
		ctrl->size(width, ctrl->getHeight());
		_check_scroll();
		_check_line_position(true);
		return true;
	}
	ctlList::rowInfo ctlList::getSelectedRow()
	{
		if(_list_select_cell_info.ctrl == nullptr)
		{
			rowInfo info;
			info.ctrl = nullptr;
			info.height = info.index =  0;
			return info;
		}
		return getRowInfo(_list_select_cell_info.row);
	}
	ctlList::cellInfo ctlList::getSelectedCell()
	{
		if(_list_select_cell_info.ctrl == nullptr)
		{
			cellInfo info;
			info.ctrl = nullptr;
			info.row = info.column = 0;
			return info;
		}
		return _list_select_cell_info;
	}
	ctlList::rowInfo ctlList::getRowInfo(unsigned index)
	{
		if(index >= getRowNumber())
		{
			rowInfo info;
			info.ctrl = nullptr;
			info.height = info.index = 0;
			return info;
		}
		rowInfo info = _list_row_info[index];
		info.index = index;
		return info;
	}
	ctlList::columnInfo ctlList::getColumnInfo(unsigned index)
	{
		if(index >= getColumnNumber())
		{
			columnInfo info;
			info.index = 0;
			info.width = 0;
			return info;
		}
		columnInfo info = _list_column_info[index];
		info.index = index;
		return info;
	}
	ctlList::cellInfo ctlList::getCellInfo(unsigned row, unsigned column)
	{
		if(row >= getRowNumber() || 
			column >= getColumnNumber())
		{
			cellInfo info;
			info.column = info.row = 0;
			info.ctrl = nullptr;
			return info;
		}
		cellInfo info;
		info.column = column;
		info.row = row;
		info.ctrl = control_cast<ctlSingleLabel*>(
			getRowInfo(row).ctrl->getChild(column));
		return info;
	}
	unsigned ctlList::_list_get_body_top()
	{
		return isHeaderEnabled() ? getHeaderHeight() - 1 : 0;
	}
	unsigned ctlList::_list_get_body_width()
	{
		if(_list_scroll_vertical.isShow())
			return getWidth() - _list_scroll_vertical.getRealScrollSize();
		return getWidth();
	}
	unsigned ctlList::_list_get_body_height()
	{
		unsigned top = _list_get_body_top();
		if(_list_scroll_horizontal.isShow())
			return getHeight() - _list_scroll_horizontal.getRealScrollSize() - top;
		return getHeight() - top;
	}
	void ctlList::_check_scroll(bool xf)
	{
		bool isEnable2Disenable = false;
		auto lambdaScrollCheck = 
			[&isEnable2Disenable, xf](ctlScroll& scroll, 
			ctlOverflowScroll overflow, unsigned logicMax) mutable
			{
				isEnable2Disenable = false;
				if(static_cast<int>(logicMax) > 0)
				{
					if(!scroll.isEnabled())
					{
						scroll.enable(true);
						if(overflow != ctlOverflowScroll::hide)
							scroll.show();
						else scroll.hide();
						scroll.setMaxLogicLength(logicMax);
					} else {
						unsigned doff = scroll.getScrolledLogicLength()
							+ logicMax - scroll.getMaxLogicLength();
						scroll.setMaxLogicLength(logicMax);
						if(xf) scroll.setScrolledLogicLength(doff);
					}
				} else {
					isEnable2Disenable = true;
					scroll.enable(false);
					if(overflow != ctlOverflowScroll::show)
						scroll.hide();
					else scroll.show();
				}
			};
		lambdaScrollCheck(_list_scroll_vertical, getOverflowY(), 
			getRowHeight() - _list_get_body_height());
		if(isEnable2Disenable) 
			_list_top_offset = getRowInfo(_list_top_pos).height;
		lambdaScrollCheck(_list_scroll_horizontal, getOverflowX(), 
			getColumnWidth() - _list_get_body_width());
		//这一次是为了防止水平滚动条占用空间以至溢出
		lambdaScrollCheck(_list_scroll_vertical, getOverflowY(), 
			getRowHeight() - _list_get_body_height());
		if(isEnable2Disenable) 
			_list_top_offset = getRowInfo(_list_top_pos).height;
		bool record1 = _list_scroll_horizontal.isEnabled();
		bool record2 = _list_scroll_vertical.isEnabled();
		_list_scroll_horizontal.enable(true);
		_list_scroll_vertical.enable(true);
		if(_list_scroll_vertical.isShow() && _list_scroll_horizontal.isShow())
		{
			_list_scroll_vertical.setRelativeSizeFormat(ctlDirection::y, 0, 
				_list_scroll_horizontal.getRealScrollSize() - 1, this, this);
			_list_scroll_horizontal.setRelativeSizeFormat(ctlDirection::x, 
				_list_scroll_vertical.getRealScrollSize() - 1, 0, this, this);
		} else {
			_list_scroll_vertical.setRelativeSizeFormat(ctlDirection::y, 0, 0, this, this);
			_list_scroll_horizontal.setRelativeSizeFormat(ctlDirection::x, 0, 0, this, this);
		}
		_list_scroll_horizontal.enable(record1);
		_list_scroll_vertical.enable(record2);
		redraw();
	}
	void ctlList::_list_row_click_handler(ctlRoot* object, ctlMouseEvent* me)
	{
		ctlList *list = control_cast<ctlList*>(object->getParent());
		if(list->isLineSelect())
		{
			unsigned row = 0;
			for(const rowInfo& elem : list->_list_row_info)
			{
				if(elem.ctrl == object)
				{
					list->setSelectedCell(row, 0);
					break;
				}
				++row;
			}
		} else {
			unsigned column = 0;
			int left = 0;
			for(const columnInfo& elem : list->_list_column_info)
			{
				if(me->mousePos.x > left 
					&& me->mousePos.x < left + static_cast<int>(elem.width) - 1)
				{
					unsigned row = 0;
					for(const rowInfo& rowElem : list->_list_row_info)
					{
						if(rowElem.ctrl == object)
						{
							list->setSelectedCell(row, column);
							break;
						}
						++row;
					}
					break;
				}
				++column;
				left += static_cast<int>(elem.width) - 1;
			}
		}
	}
	void ctlList::_list_row_dbclick_handler(ctlRoot* object, ctlMouseEvent* me)
	{
		ctlList *list = control_cast<ctlList*>(object->getParent());
		if(list->isDoubleClickEdit() == false) return;
		unsigned row = list->findRowIndex(control_cast<ctlStatic*>(object));
		unsigned column = 0;
		int left = 0;
		for(const columnInfo& elem : list->_list_column_info)
		{
			if(me->mousePos.x > left 
				&& me->mousePos.x < left 
				+ static_cast<int>(elem.width) - 1)
					break;	
			++column;
			left += static_cast<int>(elem.width) - 1;
		}
		if(column >= list->getColumnNumber()) return;
		ctlNotifyEvent ne;
		ne.eventType = ctlNotifyEventType::listEdit;
		ctlNotifyListEdit nle;
		ne.info = &nle;
		nle.column = column;
		nle.row = row;
		nle.ctrl = list->getCellInfo(row, column).ctrl;
		nle.state = nle.before;
		nle.edit = &list->_list_edit;
		bool isAllow = true;
		nle.isAllow = &isAllow;
		list->_list_edit.create(nle.ctrl, ctlPoint(0, -1), 10, nle.ctrl->getText());
		list->_list_edit.setFont(nle.ctrl->getFontIndex());
		list->_list_edit.setPercentSizeFormat(ctlDirection::x, 1.0f, 0.0f);
		list->_list_edit.setRelativeSizeFormat(ctlDirection::y, 0, 0, nullptr, nle.ctrl);
		auto lambdaSetText = [column, row, list](ctlRoot* rot, bool mode)
		{
			bool isAllow = true;
			ctlNotifyEvent ne;
			ne.eventType = ctlNotifyEventType::listEdit;
			ctlNotifyListEdit nle;
			ne.info = &nle;
			nle.column = column;
			nle.row = row;
			nle.ctrl = list->getCellInfo(row, column).ctrl;
			nle.state = nle.after;
			nle.afterIsReturn = mode;
			nle.edit = &list->_list_edit;
			nle.isAllow = &isAllow;
			list->_notify_event_(&ne);
			if(isAllow) nle.ctrl->setText(rot->getText());
		};
		list->_list_edit.addLambdaEvent(ctlKeyEventType::down, 
			[column, row, list, lambdaSetText](ctlRoot* rot, ctlKeyEvent* ke)
			{
				switch(ke->charInfo.virtualKey)
				{
				case VK_RETURN:
					lambdaSetText(rot, true);
					// 没有 break
				case VK_ESCAPE:
					rot->destroy();
					list->setFocus();
					*ke->nextHandler = false;
					break;
				}
			}); 
		list->_list_edit.setFocus();
		list->_list_edit.setCaretAppend();
		list->_list_edit.addLambdaEvent(ctlNotifyEventType::blur,
			[list, lambdaSetText](ctlRoot* r, ctlNotifyEvent*)
			{
				if(r->isCreated())
				{
					lambdaSetText(r, false);
					list->setFocus(); 
					r->destroy(); 
				}
			});
		list->_notify_event_(&ne);
		if(!isAllow) list->_list_edit.destroy();
	}
	ctlPoint ctlList::_list_map_pos_to_point(unsigned pos)
	{
		ctlPoint pt(0, _list_get_body_top());
		if(_list_row_info.empty()) return pt;
		auto iter = _list_row_info.begin() + _list_top_pos;
		pt.y -= static_cast<int>(iter->height - _list_top_offset);
		while(pos > _list_top_pos)
		{
			pt.y += static_cast<int>(iter->height) - 1;
			++iter;
			--pos;
		}
		while(pos < _list_top_pos)
		{
			--iter;
			pt.y -= static_cast<int>(iter->height) - 1;
			++pos;
		}
		return pt;
	}
	std::pair<unsigned, unsigned> ctlList::_list_map_point_to_pos(ctlPoint pt)
	{
		if(_list_row_info.empty()) 
			return std::pair<unsigned, unsigned>(0, 0);
		pt.y -= _list_get_body_top();
		unsigned pps = _list_top_pos;
		auto iter = _list_row_info.begin() + pps;
		int pos1 = static_cast<int>(_list_top_offset) - iter->height;
		while(iter != _list_row_info.end() && pt.y > pos1)
		{
			pos1 += iter->height - 1;
			++iter;
			++pps;
		}
		while(iter != _list_row_info.begin() && pt.y < pos1)
		{
			--iter;
			pos1 -= iter->height - 1;
			--pps;
		}
		if(iter == _list_row_info.end()) --pps;
		return std::pair<unsigned, unsigned>(pps, pos1 - pt.y 
			+ _list_row_info[pps].height);
	}
	void ctlList::_check_position(int delta)
	{
		_list_scroll_vertical.addScrolledLogicLength(delta);
	}
	void ctlList::_check_line_position(bool notAutoScroll)
	{
		if(_list_row_info.empty()) return;
		ctlPoint pt(0, _list_get_body_height());
		unsigned top = _list_get_body_top();
		auto iter = _list_row_info.begin() + _list_top_pos;
		int pos1 = static_cast<int>(_list_top_offset) - iter->height;
		while(iter != _list_row_info.end() && pt.y > pos1)
		{
			iter->ctrl->show();
			iter->ctrl->move(-static_cast<int>(_list_left_offset), pos1 + top);
			unsigned wd = _list_get_body_width() + _list_left_offset + 1;
			iter->ctrl->size(wd, iter->ctrl->getHeight());
			pos1 += iter->height - 1;
			++iter;
		}
		if(isHeaderEnabled())
		{
			_list_header.move(-static_cast<int>(_list_left_offset), 0);
			_list_header.size(getWidth() + _list_left_offset + 1, getHeaderHeight());
		}
	}
	std::vector<ctlRoot*>::const_pointer ctlList::_get_redraw_begin(unsigned index)
	{
		if(index + 1 == _get_redraw_or_event_number())
			return ctlRoot::_get_event_begin(0) + 1
			- getChildrenZOrderTopNumber();
		unsigned row = index + _list_top_pos;
		_list_temp_pointer = getRowInfo(index + _list_top_pos).ctrl;
		_list_temp_pointer->lockDrawing(false);
		bool allow = _list_send_event(ctlNotifyList::redrawRow, 
			getRowInfo(row).ctrl, row, nullptr);
		//设置选中的行信息
		if(allow && _list_is_cell_equal(row, _list_select_cell_info.column, _list_select_cell_info))
		{
			ctlStyleList* sl = style_cast<ctlStyleList*>(_styleBase);
			_list_set_row_style(_list_select_cell_info.row, _list_select_cell_info.column, 
				sl->selectFill.crBegin, sl->selectFill.crEnd, sl->selectTextColor);
		}
		_list_temp_pointer->unlockDrawing(false);
		return &_list_temp_pointer;
	}
	unsigned ctlList::_get_redraw_end(unsigned index)
	{
		if(index + 1 == _get_redraw_or_event_number())
			return getChildrenZOrderTopNumber();
		return 1;
	}
	std::vector<ctlRoot*>::const_pointer ctlList::_get_event_begin(unsigned index)
	{
		if(index == 0) return ctlRoot::_get_event_begin(0);
		_list_temp_pointer = getRowInfo(index + _list_top_pos - 1).ctrl;
		return &_list_temp_pointer;
	}
	unsigned ctlList::_get_event_end(unsigned index)
	{
		if(index == 0)
			return getChildrenZOrderTopNumber();
		return 1;
	}
	unsigned ctlList::_get_redraw_or_event_number()
	{
		if(_list_row_info.empty())
			return 1;
		return _list_map_point_to_pos(ctlPoint(0, _list_get_body_top() 
			+ _list_get_body_height() - 1)).first - _list_top_pos + 2;
	}
	bool ctlList::appendColumn(unsigned width)
	{
		return insertColumn(getColumnNumber(), width);
	}
	bool ctlList::appendRow(unsigned width)
	{
		return insertRow(getRowNumber(), width);
	}
	bool ctlList::_list_send_event(decltype(ctlNotifyList::afterRemoveColumn) state, 
		ctlStatic* ctrl, unsigned index, unsigned *length)
	{
		ctlNotifyEvent ne;
		ne.eventType = ctlNotifyEventType::list;
		ctlNotifyList nl;
		ne.info = &nl;
		bool allow = true;
		nl.isAllow = &allow;
		nl.ctrl = ctrl;
		nl.index = index;
		nl.state = state;
		nl.length = length;
		_notify_event_(&ne);
		return allow;
	}
	bool ctlList::_list_is_cell_equal(unsigned row, unsigned column, const cellInfo& info)
	{
		return info.ctrl 
			&& (isLineSelect() && row == info.row
			|| !isLineSelect() && row == info.row
			&& column == info.column);
	}
	bool ctlList::setSelectedCell(unsigned row, unsigned column)
	{
		if(isLineSelect() == false)
		{
			if(row >= getRowNumber() || column >= getColumnNumber())
				return false; 
		} else {
			if(row >= getRowNumber())
				return false;
			column = 0;
		}
		if(_list_is_cell_equal(row, column, _list_select_cell_info))
			return false;
		cellInfo preInfo = _list_select_cell_info;
		clearSelected();
		_list_select_cell_info = getCellInfo(row, column);
		ctlNotifyEvent ne;
		ne.eventType = ctlNotifyEventType::selectChange;
		ne.info = nullptr;
		_notify_event_(&ne);
		return true;
	}
	ctlScroll* ctlList::getVerticalScroll()
	{
		return &_list_scroll_vertical;
	}
	ctlScroll* ctlList::getHorizontalScroll()
	{
		return &_list_scroll_horizontal;
	}
	bool ctlList::isHeaderEnabled()
	{
		return _list_header_enable;
	}
	void ctlList::setHeaderEnable(bool enable)
	{
		_list_header_enable = enable;
		bool record = _list_scroll_vertical.isEnabled();
		_list_scroll_vertical.enable(true);
		if(enable)
		{
			_list_scroll_vertical.move(_list_scroll_vertical.getLeft(), getHeaderHeight() - 1);
			_list_header.show();
		} else {
			_list_scroll_vertical.move(_list_scroll_vertical.getLeft(), 0);
			_list_header.hide();
		}
		_list_scroll_vertical.enable(record);
		_check_line_position(true);
	}
	unsigned ctlList::getHeaderHeight()
	{
		return _list_header.getHeight();
	}
	void ctlList::setHeaderHeight(unsigned height)
	{
		if(height == 0)
			height = getFont()->getFontHeight() + 10;
		_list_header.size(1, height);
	}
	ctlButton* ctlList::getHeaderCtrl(unsigned index)
	{
		if(index >= getColumnNumber())
			return nullptr;
		return control_cast<ctlButton*>(_list_header.getChild(index));
	}
	ctlStatic* ctlList::getHeader()
	{
		return &_list_header;
	}
	void ctlList::clearSelected()
	{
		if(_list_select_cell_info.ctrl)
			_list_set_row_default_style(_list_select_cell_info.row);
		_list_select_cell_info.ctrl = nullptr;
	}
	void ctlList::_list_set_row_style(unsigned row, unsigned column,
		ctlColor begin, ctlColor end, ctlColor text)
	{
		if(isLineSelect())
		{
			_list_select_cell_info = getCellInfo(row, 0);
			IBackground* bk =  getRowInfo(row).ctrl;
			bk->setBkFillStyle(IBackground::fillStyle::solid);
			bk->setBkFillColor(begin);
			bk->setBkFillGradientEnd(end);
			for(ctlRoot* elem : *bk)
			{
				ctlSingleLabel* em = control_cast<ctlSingleLabel*>(elem);
				if(em) em->setTextColor(text);
			}
		} else {
			ctlSingleLabel* label = getCellInfo(row, column).ctrl;
			label->setBkFillStyle(IBackground::fillStyle::solid);
			label->setTextColor(text);
			label->setBkFillColor(begin);
			label->setBkFillGradientEnd(end);
		}
	}
	void ctlList::_list_set_row_default_style(unsigned rowIndex)
	{
		if(rowIndex >= getRowNumber())
			return;
		ctlStatic* row = getRowInfo(rowIndex).ctrl;
		row->setBkFillStyle(IBackground::fillStyle::empty);
		row->setBkFillGradientEnd(0, false);
		for(ctlRoot* elem : *row)
		{
			ctlSingleLabel* em = control_cast<ctlSingleLabel*>(elem);
			if(em) 
			{
				em->setBkFillStyle(IBackground::fillStyle::empty);
				em->setBkFillGradientEnd(0, false);
				em->setTextColor(style_cast<ctlStyleList*>(_styleBase)->textColor);
			}
		}
	}
	bool ctlList::swapRow(unsigned a, unsigned b)
	{
		if(a >= getRowNumber() || b >= getRowNumber())
			return false;
		rowInfo &rowa = _list_row_info[a], 
			&rowb = _list_row_info[b];
		if(_list_select_cell_info.row == a)
			_list_select_cell_info.row = b;
		if(_list_select_cell_info.row == b)
			_list_select_cell_info.row = a;
		rowInfo temp = rowa;
		rowa = rowb;
		rowb = temp;
		_check_line_position(true);
		return true;
	}
	bool ctlList::sortRows(unsigned begin, unsigned end, unsigned column, 
		bool(*sortCompareFunction)(std::wstring::const_pointer, std::wstring::const_pointer))
	{
		if(end <= begin || end > getRowNumber()
			|| column >= getColumnNumber())
			return false;
		//保存原先选中的值
		ctlStatic* preSelect = nullptr;
		if(_list_select_cell_info.ctrl) 
			preSelect = _list_select_cell_info.ctrl->getParent()->castTo<ctlStatic*>();
		//排序
		std::sort(_list_row_info.begin() + begin, _list_row_info.begin() + end, 
			[=](const rowInfo& a, const rowInfo& b)
			{
				return sortCompareFunction(
					a.ctrl->getChild(column)->_textpack.wptr(), 
					b.ctrl->getChild(column)->_textpack.wptr());
			});
		//恢复选中的行
		if(preSelect && begin <= _list_select_cell_info.row
			&& end >= _list_select_cell_info.row)
		{
			_list_select_cell_info.row = 
				std::find_if(_list_row_info.begin() + begin, 
					_list_row_info.begin() + end, 
					[preSelect](const rowInfo& info)
					{
						return preSelect == info.ctrl;
					}) - _list_row_info.begin();
		}
		_check_line_position(true);
		return true;
	}
	void ctlList::setStyle(const ctlStyleBase& style)
	{
		if(style_cast<const ctlStyleList*>(&style) == nullptr)
			return;
		ctlRoot::setStyle(style);
		const ctlStyleList* sl = style_cast<const ctlStyleList*>(&style);
		_list_select_cell_info.ctrl = nullptr;
		setSelectedCell(_list_select_cell_info.row, 
			_list_select_cell_info.column);
	}
	void ctlList::_position_event_(ctlPositionEvent* pe)
	{
		ctlRoot::_position_event_(pe);
		if(pe->eventType == ctlPositionEventType::size)
		_check_scroll();
	}
	void ctlList::_mouse_event_(ctlMouseEvent* me)
	{
		if(me->eventModel != ctlMouseEventModel::cut) return;
		ctlRoot::_mouse_event_(me);
		if(me->eventType == ctlMouseEventType::wheel)
			_list_scroll_vertical.dealMouseWheelEvent(me);
	}
	void ctlList::_key_event_(ctlKeyEvent* ke)
	{
		ctlRoot::_key_event_(ke);
		if(ke->eventType == ctlKeyEventType::down)
		{
			switch(ke->charInfo.virtualKey)
			{
			case VK_LEFT:
				switch(getKeyAffect())
				{
				case keyAffect::scroll:
					_list_scroll_horizontal.addScrolledLogicLength(
						-_list_scroll_horizontal.getLogicStep());
					break;
				case keyAffect::select:
					setSelectedCell(_list_select_cell_info.row, _list_select_cell_info.column - 1);
					if(isLineSelect() == false) scrollToX(_list_select_cell_info.column);
					break;
				}
				break;
			case VK_RIGHT:
				switch(getKeyAffect())
				{
				case keyAffect::scroll:
					_list_scroll_horizontal.addScrolledLogicLength(
						_list_scroll_horizontal.getLogicStep());
					break;
				case keyAffect::select:
					setSelectedCell(_list_select_cell_info.row, _list_select_cell_info.column + 1);
					if(isLineSelect() == false) scrollToX(_list_select_cell_info.column);
					break;
				}
				break;
			case VK_UP:
				switch(getKeyAffect())
				{
				case keyAffect::scroll:
					_list_scroll_vertical.addScrolledLogicLength(
						-_list_scroll_vertical.getLogicStep());
					break;
				case keyAffect::select:
					setSelectedCell(_list_select_cell_info.row - 1, _list_select_cell_info.column);
					scrollToY(_list_select_cell_info.row);
					break;
				}
				break;
			case VK_DOWN:
				switch(getKeyAffect())
				{
				case keyAffect::scroll:
					_list_scroll_vertical.addScrolledLogicLength(
						_list_scroll_vertical.getLogicStep());
					break;
				case keyAffect::select:
					setSelectedCell(_list_select_cell_info.row + 1, _list_select_cell_info.column);
					scrollToY(_list_select_cell_info.row);
					break;
				}
				break;
			}
		}
	}
	unsigned ctlList::findRowIndex(ctlStatic* object)
	{
		if(object == nullptr)
			return 0xffffffff;
		auto iter = std::find_if(_list_row_info.begin(), _list_row_info.end(), 
			[=](const rowInfo& info) { return info.ctrl == object; });
		if(iter != _list_row_info.end())
			return iter - _list_row_info.begin();
		return 0xffffffff;
	}
	void ctlList::scrollToY(unsigned row)
	{
		if(row >= getRowNumber())
			return;
		ctlPoint pt = _list_map_pos_to_point(row);
		int bnt = getHeight() - static_cast<int>(_list_row_info[row].height);
		if(pt.y < static_cast<int>(_list_get_body_top()))
			_check_position(pt.y - _list_get_body_top());
		else if(pt.y > bnt) _check_position(pt.y - bnt);
		redraw();
	}
	void ctlList::scrollToX(unsigned column)
	{
		unsigned ofs = 0;
		for(auto beg = _list_column_info.begin(), 
			end = _list_column_info.end(); beg != end; ++beg)
		{
			if(beg - _list_column_info.begin() == column)
			{
				if(ofs < _list_left_offset)
					_list_left_offset = ofs;
				break;
			}
			ofs += beg->width - 1;
		}
		unsigned afx = _list_column_info[column].width - 1;
		ofs += afx;
		if(ofs > _list_get_body_width() + _list_left_offset)
			_list_left_offset = ofs - _list_get_body_width();
		_list_scroll_horizontal.setScrolledLogicLength(_list_left_offset);
	}
	ctlList::keyAffect ctlList::getKeyAffect()
	{
		return _list_key_affect;
	}
	void ctlList::setKeyAffect(keyAffect affect)
	{
		_list_key_affect = affect;
	}
}